{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# リフレクションプログラミング\n",
    "\n",
    "* プログラミング実行中に、そのプログラムの情報を取得したり操作したりすること"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "#<Foo:0x007fdbd21d4380 @foo=1, @bar=2, @baz=3>\n",
      "[:@foo, :@bar, :@baz]\n",
      "true\n",
      "2\n",
      ":@foo\n",
      ":@bar\n",
      ":@baz\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "#<Foo:0x007fdbd21d4380 @foo=1, @bar=4, @baz=3, @qux=5>\n",
      "5\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "## オブジェクトのインスタンス変数調査\n",
    "class Foo\n",
    "  def initialize\n",
    "    @foo = 1\n",
    "    @bar = 2\n",
    "    @baz = 3\n",
    "  end\n",
    "\n",
    "  def sample_method\n",
    "  end\n",
    "end\n",
    "\n",
    "object1 = Foo.new\n",
    "\n",
    "### オブジェクトを出力\n",
    "p object1      ### 出力例) #<Foo:0x007ffda391d8b0 @foo=1, @bar=2, @baz=3>\n",
    "\n",
    "### 配列の要素としてインスタンス変数名をシンボルで返す\n",
    "p object1.instance_variables       ### 出力例) [:@foo, :@bar, :@baz]\n",
    "\n",
    "### オブジェクトに対してインスタンス変数名が定義されているか直接問い合わせる\n",
    "p object1.instance_variable_defined?(:@bar)      ### 出力例) true or false\n",
    "\n",
    "### インスタンス変数を参照\n",
    "p object1.instance_variable_get(:@bar)     ### (インスタンス変数の値が出力される)\n",
    "\n",
    "### 要素を取り出す\n",
    "object1.instance_variables.select do |v|\n",
    "  p v\n",
    "end\n",
    "### 出力例) :@foo\n",
    "###        :@bar\n",
    "###        :@baz\n",
    "\n",
    "### 各要素にあるインスタンス変数の値を取り出す\n",
    "object1.instance_variables.select do |v|\n",
    "  p object1.instance_variable_get(v)\n",
    "end\n",
    "### 出力例) 1\n",
    "###        2\n",
    "###        3\n",
    "\n",
    "### インスタンス変数の値を更新する\n",
    "object1.instance_variable_set(:@bar, 4)\n",
    "p object1.instance_variable_get(:@bar)      ### 出力例) 4\n",
    "\n",
    "### インスタンス変数の値を更新する\n",
    "object1.instance_variable_set(:@qux, 5)\n",
    "p object1\n",
    "p object1.instance_variable_get(:@qux)\n",
    "### 出力例) #<Foo:0x007f970996f008 @foo=1, @bar=2, @baz=3, @qux=5>\n",
    "###        5"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "deletable": true,
    "editable": true
   },
   "source": [
    "# オブジェクトが持つメソッドの確認"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\"メソッドの一覧表示\"\n",
      ":singleton_method\n",
      ":public_method\n",
      ":protected_method\n",
      ":super_public_method\n",
      ":super_protected_method\n",
      ":pry\n",
      ":__binding__\n",
      ":pretty_print\n",
      ":pretty_print_cycle\n",
      ":pretty_print_instance_variables\n",
      ":pretty_print_inspect\n",
      ":to_json\n",
      ":instance_of?\n",
      ":public_send\n",
      ":instance_variable_get\n",
      ":instance_variable_set\n",
      ":instance_variable_defined?\n",
      ":remove_instance_variable\n",
      ":private_methods\n",
      ":kind_of?\n",
      ":instance_variables\n",
      ":tap\n",
      ":define_singleton_method\n",
      ":is_a?\n",
      ":extend\n",
      ":to_enum\n",
      ":enum_for\n",
      ":<=>\n",
      ":pretty_inspect\n",
      ":===\n",
      ":=~\n",
      ":!~\n",
      ":eql?\n",
      ":respond_to?\n",
      ":freeze\n",
      ":inspect\n",
      ":display\n",
      ":object_id\n",
      ":send\n",
      ":gem\n",
      ":to_s\n",
      ":method\n",
      ":nil?\n",
      ":hash\n",
      ":class\n",
      ":singleton_class\n",
      ":clone\n",
      ":dup\n",
      ":itself\n",
      ":taint\n",
      ":tainted?\n",
      ":untaint\n",
      ":untrust\n",
      ":trust\n",
      ":untrusted?\n",
      ":methods\n",
      ":protected_methods\n",
      ":frozen?\n",
      ":public_methods\n",
      ":singleton_methods\n",
      ":!\n",
      ":==\n",
      ":!=\n",
      ":__send__\n",
      ":equal?\n",
      ":instance_eval\n",
      ":instance_exec\n",
      ":__id__\n",
      "\"=====================\"\n",
      "\"publicメソッドの表示\"\n",
      ":singleton_method\n",
      ":public_method\n",
      ":super_public_method\n",
      ":pry\n",
      ":__binding__\n",
      ":pretty_print\n",
      ":pretty_print_cycle\n",
      ":pretty_print_instance_variables\n",
      ":pretty_print_inspect\n",
      ":to_json\n",
      ":instance_of?\n",
      ":public_send\n",
      ":instance_variable_get\n",
      ":instance_variable_set\n",
      ":instance_variable_defined?\n",
      ":remove_instance_variable\n",
      ":private_methods\n",
      ":kind_of?\n",
      ":instance_variables\n",
      ":tap\n",
      ":define_singleton_method\n",
      ":is_a?\n",
      ":extend\n",
      ":to_enum\n",
      ":enum_for\n",
      ":<=>\n",
      ":pretty_inspect\n",
      ":===\n",
      ":=~\n",
      ":!~\n",
      ":eql?\n",
      ":respond_to?\n",
      ":freeze\n",
      ":inspect\n",
      ":display\n",
      ":object_id\n",
      ":send\n",
      ":gem\n",
      ":to_s\n",
      ":method\n",
      ":nil?\n",
      ":hash\n",
      ":class\n",
      ":singleton_class\n",
      ":clone\n",
      ":dup\n",
      ":itself\n",
      ":taint\n",
      ":tainted?\n",
      ":untaint\n",
      ":untrust\n",
      ":trust\n",
      ":untrusted?\n",
      ":methods\n",
      ":protected_methods\n",
      ":frozen?\n",
      ":public_methods\n",
      ":singleton_methods\n",
      ":!\n",
      ":==\n",
      ":!=\n",
      ":__send__\n",
      ":equal?\n",
      ":instance_eval\n",
      ":instance_exec\n",
      ":__id__\n",
      "\"=====================\"\n",
      "\"privateメソッドの表示\"\n",
      ":private_method\n",
      ":super_private_method\n",
      ":timeout\n",
      ":Digest\n",
      ":DelegateClass\n",
      ":sprintf\n",
      ":format\n",
      ":Integer\n",
      ":Float\n",
      ":String\n",
      ":Array\n",
      ":Hash\n",
      ":catch\n",
      ":throw\n",
      ":loop\n",
      ":block_given?\n",
      ":Rational\n",
      ":Complex\n",
      ":trace_var\n",
      ":untrace_var\n",
      ":JSON\n",
      ":at_exit\n",
      ":jj\n",
      ":set_trace_func\n",
      ":caller\n",
      ":caller_locations\n",
      ":select\n",
      ":test\n",
      ":fork\n",
      ":exit\n",
      ":`\n",
      ":gem_original_require\n",
      ":j\n",
      ":sleep\n",
      ":pp\n",
      ":URI\n",
      ":respond_to_missing?\n",
      ":load\n",
      ":exec\n",
      ":exit!\n",
      ":system\n",
      ":spawn\n",
      ":abort\n",
      ":open\n",
      ":syscall\n",
      ":print\n",
      ":printf\n",
      ":puts\n",
      ":putc\n",
      ":readlines\n",
      ":Pathname\n",
      ":p\n",
      ":readline\n",
      ":gets\n",
      ":srand\n",
      ":rand\n",
      ":proc\n",
      ":lambda\n",
      ":initialize_copy\n",
      ":initialize_clone\n",
      ":initialize_dup\n",
      ":trap\n",
      ":require\n",
      ":require_relative\n",
      ":autoload\n",
      ":autoload?\n",
      ":binding\n",
      ":local_variables\n",
      ":warn\n",
      ":raise\n",
      ":fail\n",
      ":global_variables\n",
      ":__method__\n",
      ":__callee__\n",
      ":__dir__\n",
      ":eval\n",
      ":iterator?\n",
      ":method_missing\n",
      ":singleton_method_added\n",
      ":singleton_method_removed\n",
      ":singleton_method_undefined\n",
      ":initialize\n",
      "\"=====================\"\n",
      "\"protectedメソッドの表示\"\n",
      ":protected_method\n",
      ":super_protected_method\n",
      "\"=====================\"\n",
      "\"singletonメソッドの表示\"\n",
      ":singleton_method\n",
      "\"=====================\"\n",
      "\"引数にfalseを渡すとsingletonメソッドのみ表示\"\n",
      "[:singleton_method]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[:singleton_method]"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# オブジェクトが持つメソッドの確認\n",
    "class ParentClass\n",
    "  def super_public_method\n",
    "    'super_public_method'\n",
    "  end\n",
    "\n",
    "  private\n",
    "\n",
    "  def super_private_method\n",
    "    'super_private_method'\n",
    "  end\n",
    "\n",
    "  protected\n",
    "\n",
    "  def super_protected_method\n",
    "    'super_protected_method'\n",
    "  end\n",
    "end\n",
    "\n",
    "class ChildClass < ParentClass\n",
    "\n",
    "  def public_method\n",
    "  end\n",
    "\n",
    "  private\n",
    "\n",
    "  def private_method\n",
    "  end\n",
    "\n",
    "  protected\n",
    "\n",
    "  def protected_method\n",
    "  end\n",
    "end\n",
    "\n",
    "child = ChildClass.new\n",
    "\n",
    "def child.singleton_method\n",
    "end\n",
    "\n",
    "p \"メソッドの一覧表示\"\n",
    "child.methods.each do |v|\n",
    "  p v\n",
    "end\n",
    "\n",
    "p \"=====================\"\n",
    "p \"publicメソッドの表示\"\n",
    "child.public_methods.each do |v|\n",
    "  p v\n",
    "end\n",
    "\n",
    "p \"=====================\"\n",
    "p \"privateメソッドの表示\"\n",
    "child.private_methods.each do |v|\n",
    "  p v\n",
    "end\n",
    "\n",
    "p \"=====================\"\n",
    "p \"protectedメソッドの表示\"\n",
    "child.protected_methods.each do |v|\n",
    "  p v\n",
    "end\n",
    "\n",
    "p \"=====================\"\n",
    "p \"singletonメソッドの表示\"\n",
    "child.singleton_methods.each do |v|\n",
    "  p v\n",
    "end\n",
    "\n",
    "p \"=====================\"\n",
    "p \"引数にfalseを渡すとsingletonメソッドのみ表示\"\n",
    "p child.methods(false)\n",
    "\n",
    "# (以下今の段階では飛ばし)\n",
    "\n",
    "# オブジェクトにメソッドが定義されているかを確認する\n",
    "\n",
    "# has_method = ParentClass.new\n",
    "# ### super_public_methodがあるのでtrue\n",
    "# p has_method.respond_to?(:super_public_method)\n",
    "#\n",
    "# ### privateのメソッドはデフォルトではfalse\n",
    "# p has_method.respond_to?(:super_private_method)\n",
    "#\n",
    "# ### 第二引数をtrueにするとprivate_methodも確認できるようになる\n",
    "# p has_method.respond_to?(:super_private_method, true)\n",
    "\n",
    "# (以下今の段階では飛ばし)\n",
    "\n",
    "# Object#sendで任意のメソッドを呼び出す\n",
    "\n",
    "### protectedやprivateメソッドも呼び出せる\n",
    "# send_method = ParentClass.new\n",
    "# p send_method.send(:super_public_method)\n",
    "# p send_method.send(:super_protected_method)\n",
    "# p send_method.send(:super_private_method)\n",
    "\n",
    "### ライブラリを作るときはライブラリ使用者側でsendがオーバーライドされている可能性を考慮して__send__を使う\n",
    "# p send_method.__send__(:super_public_method)\n",
    "\n",
    "# ----------------------- #\n",
    "# クラスについて調べる"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false,
    "deletable": true,
    "editable": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[:@@class_val]\n",
      "[:@@class_val, :@@lazy_class_val]\n",
      "true\n",
      "\"fooclassval\"\n",
      "\"hoge\"\n",
      "[:CLASS_CONST_VAR, :InnerFooClassValiables, :MODULE_CONST_VAR]\n",
      "[:CLASS_CONST_VAR, :InnerFooClassValiables]\n",
      "[:INNER_CLASS_CONST_VAR]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[:INNER_CLASS_CONST_VAR]"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "module FooModule\n",
    "  MODULE_CONST_VAR = 1\n",
    "end\n",
    "\n",
    "class FooClassValiables\n",
    "\n",
    "  include FooModule\n",
    "  CLASS_CONST_VAR = 2\n",
    "  @@class_val = \"fooclassval\"\n",
    "\n",
    "  class InnerFooClassValiables\n",
    "    INNER_CLASS_CONST_VAR = 3\n",
    "  end\n",
    "\n",
    "  def def_lazy_class_val\n",
    "    @@lazy_class_val = \"foolazyclassval\"\n",
    "  end\n",
    "end\n",
    "\n",
    "### 呼び出し時点で定義されているクラス変数の一覧を表示\n",
    "p FooClassValiables.class_variables\n",
    "\n",
    "### lazy_class_valを定義して呼び出し\n",
    "FooClassValiables.new.def_lazy_class_val\n",
    "p FooClassValiables.class_variables\n",
    "\n",
    "### クラス変数が定義されているか直接問い合わせる\n",
    "p FooClassValiables.class_variable_defined?('@@class_val')\n",
    "\n",
    "### クラス変数の値を取得\n",
    "p FooClassValiables.class_variable_get('@@class_val')\n",
    "\n",
    "### クラス変数に値を設定\n",
    "FooClassValiables.class_variable_set('@@class_val', 'hoge')\n",
    "p FooClassValiables.class_variable_get('@@class_val')\n",
    "\n",
    "### 定数の一覧取得\n",
    "p FooClassValiables.constants\n",
    "\n",
    "# 第二引数をfalseにすると継承したクラスやincludeしたモジュールの定数は表示されない\n",
    "p FooClassValiables.constants(false)\n",
    "\n",
    "# innerクラスの定数のみ表示\n",
    "p FooClassValiables::InnerFooClassValiables.constants"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ruby 2.3.3",
   "language": "ruby",
   "name": "ruby"
  },
  "language_info": {
   "file_extension": ".rb",
   "mimetype": "application/x-ruby",
   "name": "ruby",
   "version": "2.3.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
